
/// @file Class to manage a Processes Startup Configurations.

#ifndef NOSAL_EXEC_INTERNAL_STARTUP_CONFIGURATION_H_
#define NOSAL_EXEC_INTERNAL_STARTUP_CONFIGURATION_H_

#include "config/enter_exit_timeout.h"
#include "config/execution_manifest.h"
#include "exec_arguments.h"
//#include "ara/exec/process_specifier.h"
#include "nosal/exec/execution_client.h"
#include "nosal/log/logger.h"
#include "nosal/log/logging.h"
#include "logger.h"

#include <cstdint>
#include <functional>
#include <set>
#include <map>
#include <vector>

// Forward declaration of struct cgroup defined in libcgroup.h
//struct cgroup;

namespace netaos {
namespace nosal {
namespace exec {
namespace internal {

/// @brief Process states.
enum class ProcessState
{
    kIdle,  ///< Process has not been created and its resources are not allocated.
    kStarting,  ///< Process has been created and its resources are allocated.
    kRunning,  ///< Process has been scheduled and reported kRunning to EM.
    kTerminating,  ///< EM has sent SIGTERM signal to the process.
    kTerminated  ///< Process has terminated and its resources have been freed.
};

/// @brief TerminationBehavior
///
/// This enumeration provides options for controlling of how a Process terminates.
enum class TerminationBehavior
{
    kProcessIsNotSelfTerminating = 0,  ///< The Process terminates only on request from Execution Management.
    kProcessIsSelfTerminating = 1  ///< The Process is allowed to terminate without request from Execution Management.
};

/// @brief Cause of Process termination.
enum class TerminationCause
{
    kNormalExit,  ///< Process has been terminated normally.
    kAbnormalExit,  ///< Process has been terminated abnormally.
    kUnknown  ///< Process has ignored any signals as it may not be exist.
};

/// @brief Class to manage Processes Startup Configurations.
///
/// This class represents one Startup Configuration aggregated by the Meta Model Process.
///
/// @note Meta Model Processes are not equal to Posix processes.
class StartupConfiguration final
{
public:
    typedef std::map<std::string, ProcessState> DependencyCollectionType;
 //   typedef std::function<void(PipeMessage&& message)> ChildMessageSender;

    /// @brief Constructor of StartupConfiguration instance.
    ///
    /// @param installDir
    /// @param executable_path
    /// @param executable_name
    /// @param manifest
    /// @param startupConfig
    /// @param machineSpecificEnvironmentVariables the environment variables from Machine Manifest
    StartupConfiguration(const std::string& installDir,
        const std::string& executable_path,
        const std::string& executable_name,
        const config::ExecutionManifest& manifest,
        const config::StartupConfiguration& startupConfig,
        const std::vector<std::string>& machineSpecificEnvironmentVariables);

    StartupConfiguration(StartupConfiguration&& other);

    StartupConfiguration(StartupConfiguration&) = delete;
    StartupConfiguration& operator=(const StartupConfiguration&) = delete;
    StartupConfiguration& operator=(StartupConfiguration&&) = delete;

    /// @brief Destructor of StartupConfiguration instance.
    ~StartupConfiguration();

    /// @brief Start a new Posix process for the Startup Configuration.
    ///
    /// This call will fork the current process and then call execv() to replace
    /// the fork with the executable of the Process.
    bool Start();

    /// @brief Send a SIGTERM to the Posix process.
    void Term();

    /// @brief Send a SIGKILL to the Posix process.
    void Kill();

    /// @brief Send the given signal to the Posix process.
    ///
    /// @signum the signal to be sent.
    void SendSignal(int signum);

    /// @brief Returns the name of the Process.
    ///
    /// @returns The Process's name.
    std::string GetName() const;

    /// @brief Returns the Posix process ID.
    ///
    /// @returns the Posix process ID.
    pid_t GetPid() const;

    /// @brief Returns the short name of the config specified in the Execution Manifest.
    ///
    /// @returns String with the config_short_name.
    std::string GetConfigShortName() const noexcept;

    /// @brief Returns the name of the executable configured in this Startup Configuration.
    ///
    /// @returns The path to the Process executable.
    std::string GetExecutable() const;

    /// @brief Returns the execution_error of the StartupConfiguration
    ///
    /// @returns execution_error of the StartupConfiguration
    std::uint32_t GetExecutionError() const noexcept;

    /// @brief Returns the function_cluster_affiliation of the StartupConfiguration
    ///
    /// @returns function_cluster_affiliation of the StartupConfiguration as string
    const std::string& GetFunctionClusterAffiliation() const noexcept;

    /// @brief Returns the dependency set of this Startup Configuration.
    ///
    /// @returns The list of this Startup Configuration dependencies.
    const DependencyCollectionType& GetDependencies() const;

    /// @brief Returns the <PREFIX> directory of an Process.
    ///
    /// @returns The prefix of the Process Executable path.
    ///          This will typically be of the form "/opt/<Process name>".
    const std::string& GetPrefix() const;

    /// @brief Get the scheduling policy integer from name
    ///
    /// From the input string that contains a policy, return the integer
    /// corresponding to the policy that can be fed to sched_param structure.
    ///
    /// @param policy reference to the string that has the scheduling policy name
    /// @returns int representing the scheduling policy
    int GetSchedulingPolicyFromString(std::string const& policy);

    /// @brief Returns the state of the Startup Configuration.
    ///
    /// @returns Current state of Process in this Startup Configuration.
    ProcessState GetState() const;

    /// @brief Returns the termination behavior of the Startup Configuration.
    ///
    /// @returns Termination Behavior of the Process in this Startup Configuration.
    TerminationBehavior GetTerminationBehavior() const;

    /// @brief Returns the termination cause of the Posix process.
    ///
    /// @returns Termination cause of Process of this Startup Configuration.
    TerminationCause GetTerminationCause() const;

    /// @brief Returns the number of restart attempts if start of the process fails.
    ///
    /// @returns Number of restart attempts as extracted from the Execution Manifest.
    std::uint32_t GetNumberOfRestartAttempts() const;

    /// @brief CheckState check the process state.
    ///
    /// This method wraps a call to waitpid(). It first checks whether the stored PID is valid (i.e.
    /// the process has been  started and pid != 0). If so, it calls waitpid() to determine the current
    /// state of the process.
    ///
    /// @returns Currently checked state of Process in this Startup Configuration.
    //    ProcessState CheckState();

    /// @brief Sets the internal Posix process state value.
    void SetRunningState();

    /// @brief Sets the internal Posix process state value.
    void SetTerminatingState();

    /// @brief Sets a cause of termination for this Process.
    ///
    /// @param cause A new cause of termination.
    void SetTerminationCause(TerminationCause termination);

    /// @brief Checks if Enter Timeout is over.
    ///
    /// @returns true if the timeout is over and false in other cases including when it is not relevant.
    bool IsEnterTimeoutOver() const;

    /// @brief Checks if Exit Timeout is over.
    ///
    /// @returns true if the timeout is over and false in other cases including when it is not relevant.
    bool IsExitTimeoutOver() const;

    /// @brief Gets the type of the Process. True for if the Process belongs to some Platform Application.
    ///
    /// @returns true if the Process is a part of some Platform Application otherwise false.
    bool IsPlatformApp() const;
#if 0
    static void ProcessExecutionStateReports(
        std::set<std::shared_ptr<StartupConfiguration>>& executedStartupConfigurations);

    static std::vector<std::pair<PipeMessage, std::shared_ptr<StartupConfiguration>>> PollForStateClientRequests(
        std::set<std::shared_ptr<StartupConfiguration>>& executedStartupConfigurations);
    ///
    /// @brief CheckProcesses Poll Operating System for terminated child processes
    /// @param executedStartupConfigurations
    /// @return number of terminated child processes
    ///
    static std::size_t CheckProcesses(std::set<std::shared_ptr<StartupConfiguration>>& executedStartupConfigurations);

    void SendMessageToStateClient(PipeMessage&& message);
    /// @brief Sets the ProcessSpecifier for the find process api
    /// @param process_specifier ProcessSpecifier or nullopt if the process cannot be identified
    void SetProcessSpecifier(netaos::nosal::core::Optional<ProcessSpecifier> process_specifier);

    /// @brief Get the process specifier for the find process lookup
    ///
    /// @returns ProcessSpecifier to for the find process api
    const netaos::nosal::core::Optional<ProcessSpecifier>& GetProcessSpecifierRef() const;

    /// @brief Get the potential process specifier from the manifest
    ///
    /// @returns ProcessSpecifier to for the find process api
    netaos::nosal::core::Optional<ProcessSpecifier> GetProcessSpecifierFromManifest() const;
#endif

private:
#if 0
    const Pipe& GetExecStatePipe() const
    {
        return execStatePipe_;
    }
    const Pipe& GetStateClientRequestPipe() const
    {
        return stateClientRequestPipe_;
    }
    const Pipe& GetStateClientResponsePipe() const
    {
        return stateClientResponsePipe_;
    }

    /// @brief Sends a response message to ExecutionClient
    ///
    /// @param reponseStatus The status report to be returned to ExecutionClient
    void SendMessageToExecutionClient(const netaos::nosal::exec::ExecutionStateResponseStatus& responseStatus);

#endif
    /// @brief Sets a new state for this Process.
    ///
    /// @param cause A new state.
    void SetState(ProcessState state);
#if 0
    /// @brief Updates the pid lookup for this process and sets its pid to the given value
    ///
    /// @param pid pid of process
    /// @returns true, on success
    void UpdateFindProcessLookup(const pid_t pid);

    /// @brief Updates the pid lookup. Sets all occurences with given pid to terminated.
    ///
    /// @param pid pid to remove
    void ResetFindProcessLookup(const pid_t pid);
#endif
    /// @brief Returns a current state of this Process.
    ///
    /// @returns The state string.
    static std::string GetProcessStateString(ProcessState state);

    /// @brief Returns a termination cause of this Process.
    ///
    /// @returns The termination cause string.
    static std::string GetTerminationCauseString(TerminationCause termination);

#if 0
    /// @brief Processes ExecutionState reports received from applications
    void ProcessExecStateReports();
    /// @brief Checks what caused abnormal exit of a process
    ///
    /// @param waitpidStatus - process status information received from waitpid call
    /// @param stateBeforeTermination - process status before termination
    /// @return string with information about the cause of the termination
    ///
    static std::string GetUnexpectedTerminationCause(int waitpidStatus, ProcessState stateBeforeTermination);

#endif
    /// @brief Get the TerminationBehavior from the string
    ///
    /// From the string after "termination_behavior" in the ExecutionManifest, the corresponding value is extracted.
    ///
    /// @param termination_behavior_string the string read from the Execution Manifest
    /// @returns TerminationBehavior enum value
    ///
    TerminationBehavior termationBehaviorFromString(const std::string& termination_behavior_string);

    /// @brief Path to dir containing [bin] and [etc] dirs.
    const std::string prefix_;

    /// @brief Short name of the StartupConfig specified in the Execution Manifest.
    const std::string config_short_name_;

    /// @brief Full path to this configuration executable file.
    const std::string executable_path_;

    /// @brief Name of this configuration executable file.
    const std::string executable_name_;

    /// @brief ExecutionError as configured in the execution manifest
    const std::uint32_t execution_error_;

    /// @brief function_cluster_affiliation
    const std::string function_cluster_affiliation_;

    /// @brief Execution manifest.
    const config::ExecutionManifest& manifest_;

    /// @brief Number of restart attempts if start of the process is not successful.
    ///
    /// Extracted from the Execution Manifest.
    std::uint32_t number_of_restart_attempts_;

    /// @brief Process ID.
    pid_t pid_;

    /// @brief Enter Exit Timeout.
    config::EnterExitTimeout enter_exit_timeout_;

    /// @brief Execution arguments.
    ExecArguments arguments_;

    /// @brief This Process environment variables.
    std::vector<std::string> environment_;

    /// @brief Process Scheduling Policy
    std::string scheduling_policy_;

    /// @brief Process Scheduling Priority
    std::uint32_t scheduling_priority_;

    /// @brief Cores the process should run on
    const std::vector<std::uint32_t> core_affinity_shall_run_on_;

    /// @brief Cores the process should not run on
    const std::vector<std::uint32_t> core_affinity_shall_not_run_on_;

    /// @brief Non-Transitive set of dependencies from this Process Startup Configuration to other Processes.
    ///
    /// Contains a set of Process names that must reach the specified state,
    /// before this Startup Configuration can be started.
    DependencyCollectionType dependencies_;

    /// @brief Reporting behavior of the Process obtained from the Execution Manifest.
    config::ExecutionManifest::ExecutionStateReportingBehavior reporting_behavior_;

    /// @brief Posix process termination cause.
    TerminationCause termination_;

    ///@brief Termination behavior
    TerminationBehavior termination_behavior_;

    /// @brief Posix process state.
    ProcessState state_;

    /// @brief Timestamp in nanoseconds for a start of a new instance.
    std::int64_t enter_timestamp_;

    /// @brief Timestamp in nanoseconds for a request for a process termination.
    std::int64_t exit_timestamp_;

    /// @brief Flag that this Process is a part of some Platform Application.
    bool is_platform_app_;
#if 0
    /// @brief Posix process cgroup.
    cgroup* cgroup_;

    /// @brief ProcessSpecifier to identify the process in the find process lookup
    netaos::nosal::core::Optional<ProcessSpecifier> process_specifier_;
#endif
#if 0
    Pipe execStatePipe_;
    Pipe execStateResponsePipe_;
    Pipe stateClientRequestPipe_;
    Pipe stateClientResponsePipe_;

    PipeOutput execStatePipeOutput_;
    PipeInput execStatePipeInput_;
    PipeOutput stateClientRequestPipeOutput_;
    PipeInput stateClientResponsePipeInput_;

    std::function<void(PipeMessage&& message)> stateClientResponseSender_;
#endif
};

}  // namespace internal
}  // namespace exec
}  // namespace nosal
}  // namespace netaos

#endif  // NOSAL_EXEC_INTERNAL_STARTUP_CONFIGURATION_H_
